/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1/commercial
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may use this file in compliance with the License.
 * Alternatively, if you can't copy with the License, namely if you do not
 * want to provide source code of your modifications to this file, you may
 * negotiate a commercial licence with the initial developer.
 * You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is COID.
 *
 * The Initial Developer of the Original Code is
 * PosAm.
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Brano Kemen
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#ifndef __COID_SERVER_CONNECTION__HEADER_FILE__
#define __COID_SERVER_CONNECTION__HEADER_FILE__

#include "coid/comm/dynarray.h"
#include "coid/comm/str.h"
#include "coid/comm/binstream/binstreambuf.h"
#include "coid/comm/binstream/netstreamcoid.h"
#include "serverface.h"
#include "service.h"
#include "serviceinst.h"

namespace coid {

////////////////////////////////////////////////////////////////////////////////
///Connection (client)
struct ConnectionCoid : CoidNode
{
    virtual token short_name() const        { static token _T("netconn"); return _T; }

    
    dynarray<ServiceInstanceCoid*>  _inst;      ///< array of redirectors for hooked service instances
    local<binstream>    _bin;
    netAddress          _client_addr;
    charstr             _nick;
    uint                _timeout;
    uint                _timetoexit;
    uint                _flags;

    enum {
        PACKET_TIMESLICE            = 20,

        fEXIT                   = 0x01,
        fSTOPPED                = 0x02,
        fREMOTE                 = 0x04,
        fBSTREAM                = 0x08,
        fEXITWAIT               = 0x10,
        fKILLOWN                = 0x20,
    };

    bool should_exit() const        { return (_flags & fEXIT) != 0; }

    //opcd unlink( CoidNode* node );

    opcd loop();
    opcd dispatch_loop();
    opcd dispatch_call( binstream& bin, uint obj, uint method, metastream* meta );


    void set_active_conn();
    static void clear_active_conn();
    static ConnectionCoid* get_active_conn();
/*
    static pthread_key_t get_conn_key()
    {
        static pthread_key_t  __tk;
        if(!__tk)
            pthread_key_create( &__tk, 0 );
        return __tk;
    }
*/

    static thread_key   _thread_key;

    charstr& get_client_addr( charstr& buf ) const
    {
        return _client_addr.getHostName( buf, true );
    }

    netAddress& get_client_addr( netAddress& buf ) const
    {
        return buf = _client_addr;
    }


    opcd list_cmds( const token& cmd, dynarray<charstr>& cmds ) const;
    opcd dispatch_cmd( const token& cmd, command_tokens& arg, txtstream& reply );


    opcd attach_service_inst( uint soid, const token& name, const version& svcver, binstream& arg );

    uint add_service_inst( ServiceInstanceCoid* svci );

    void get_local_address( netAddress* adr )
    {
        if (_flags & fBSTREAM)
            netAddress::getLocalHost(adr);
        else
            get_server_address_nl(adr);
    }

    opcd init( bool remote, int socket, const netAddress& addr, charstr& nick )
    {
        _bin = new netstreamcoid(socket);
        _client_addr = addr;

        charstr name = "dispatcher for ";
        charstr host;
        name += addr.getHostName( host, true );
        object_name( name );

        _flags |= remote ? fREMOTE : 0;
        _flags |= fBSTREAM;
        _nick.takeover(nick);
        return 0;
    }

    opcd init( binstream* bin, const netAddress& addr, charstr& nick )
    {
        _bin = bin;
        _client_addr = addr;

        charstr name = "dispatcher for ";
        charstr host;
        //name += addr.getHostName( host, true );
        name += addr.getHost( host, true );
        object_name( name );

        _flags |= fREMOTE;
        _nick.takeover(nick);
        return 0;
    }

    ConnectionCoid()
    {
        _flags = fKILLOWN;
        _timeout = 0;
        _timetoexit = 0;
        _client_addr.set("", 0, false);
		_mutex.set_name( "ConnectionCoid" );
    }

    uints find_svci( uint id ) const
    {
        uints i;
        for (i=0; i<_inst.size(); ++i)
            if( _inst[i] != 0  &&  _inst[i]->get_id() == id)  break;
        if (i < _inst.size())
            return i;
        return UMAX32;
    }

    virtual opcd dispatch( uint method, binstream& bin, metastream* meta )
    {
        if( method == StdDispatchMethod::PREDESTRUCTOR )
        {
            return predestroy();
        }
        return CoidNode::dispatch( method, bin, meta );
    }

    //bool destroy();
    opcd predestroy();

    virtual ~ConnectionCoid();

    IMPLEMENTS_VIRTUAL_INTERFACE_ttree_ifc_BEGIN_PURE(ConnectionCoid,"0.0",CoidNode);
    virtual void predestroy(void*p)  { ((ConnectionCoid*)p)->predestroy(); }
    IMPLEMENTS_VIRTUAL_INTERFACE_ttree_ifc_END;

    binstream& dump_method_desc( binstream& bin, uint obj, uint method ) const
    {
        const ServiceDescriptor::MethodDesc* metdsc = &_inst[obj]->_desc->_methflags[method];

        bin << "while dispatching a call to the object " << obj;
        if( obj < _inst.size()  &&  _inst[obj] != 0 )
            bin << " (" << _inst[obj]->class_name() << ": " << _inst[obj]->object_name() << ") ";
        
        if( metdsc->name.is_empty() )
            bin << "method: " << method;
        else
            bin << "method: " << metdsc->name;

        return bin;
    }

};

} // namespace coid

#endif //__COID_SERVER_CONNECTION__HEADER_FILE__
